home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / scsh-0.4 / scsh-0 / scsh-0.4.2 / proc2.c < prev    next >
Text File  |  1995-10-29  |  12KB  |  379 lines

  1. /* Copyright (c) 1993 by Olin Shivers.
  2. **
  3. ** Please imagine a long, tedious, legalistic 5-page gnu-style copyright
  4. ** notice appearing here to the effect that you may use this code any
  5. ** way you like, as long as you don't charge money for it, remove this
  6. ** notice, or hold me liable for its results.
  7. */
  8.  
  9. /* If the above copyright notice is a problem for your app, send me mail. */
  10.  
  11. /* Using the #! interpreter hack in Unix for making scripts has a big
  12. ** problem: you only get 1 argument after the interpreter on the #! line. 
  13. ** This subroutine helps to fix that.
  14. **
  15. ** Below is a procedure that will arrange for a command-line switch of the 
  16. ** form \ <script> to stand for reading more args from line 2 of the file
  17. ** <script>. Replace the \ arg with these args. Now you can have Scheme,
  18. ** Postscript, Forth, Lisp, Smalltalk, tcl, etc. scripts that look like:
  19. **
  20. ** File foo:
  21. **         #!/usr/local/bin/scheme \
  22. **         -heap 4000000 -batch -script
  23. **         !#
  24. **         (define foo ...) ; Scheme code from here on.
  25. **         ...
  26. **
  27. ** With this program definition, executing
  28. **     foo arg1 arg2 arg3
  29. ** will turn into
  30. **     /usr/local/bin/scheme \ foo arg1 arg2 arg3
  31. ** which your Scheme interpreter main() (using this routine) will expand during
  32. ** argv processing into:
  33. **     /usr/local/bin/scheme -heap 4000000 -batch -script foo arg1 arg2 arg3
  34. ** That is, the argument processing in main() will *replace* the \ argument
  35. ** with the arguments read in from line 2 of foo. So we have dodged the
  36. ** only-one-argument-on-the-#!-line constraint.
  37. **
  38. ** The only other thing that needs to be done in this case is arrange for the
  39. ** interpreter to ignore these initial few non-Scheme lines. We can arrange
  40. ** for this in our Scheme example by defining a Scheme read macro #! that
  41. ** skips characters until newline, bang, splat (somewhat like the ; read macro
  42. ** skips characters until newline).
  43. **
  44. ** Using backslash as the meta-argument switch is handy for two reasons:
  45. ** - It is only one character. Since many Unix systems limit the #!
  46. **   line to 32 characters total, this is important.
  47. ** - It is a helpful visual pun -- implying a continuation line for the
  48. **   arguments.
  49. ** It is also very unlikely to be an already-used switch. However, -2
  50. ** is also a reasonable choice.
  51. **
  52. ** All you have to do to get this second-line meta-argument functionality is
  53. ** link this file in with your interpreter.  You can tweak this routine for
  54. ** various interpreters if you need to have it, for example, skip an initial
  55. ** comment character when it begins to scan the second line.
  56. **
  57. ** Arguments are parsed from the second line as follows:
  58. ** - The only special chars are space, tab, newline, and \.
  59. ** - Every space char terminates an argument. 
  60. **   Multiple spaces therefore introduce empty-string arguments.
  61. ** - A newline terminates the argument list, and will also terminate a
  62. **   non-empty argument (but a newline following a space does not introduce
  63. **   a final "" argument; it only terminates the argument list).
  64. ** - Tab is not allowed.
  65. **   This is to prevent you from being screwed by thinking you had several
  66. **   spaces where you really had a tab, and vice-versa.
  67. ** - The only other special character is \, the knock-down character. 
  68. **   \ escapes \, space, tab, and newline, turning off their special 
  69. **   functions. The ANSI C escape sequences, such as \n and \t are 
  70. **   supported; these also produce argument-constituents -- \n doesn't act 
  71. **   like a terminating newline. \nnn for *exactly* three octal digits reads 
  72. **   as the char whose ASCII code is nnn. It is an error if \ is followed by 
  73. **   just 1 or 2 octal digits: \3Q is an error. Octal-escapes are always 
  74. **   constituent chars. \ followed by other chars is not allowed (so we can
  75. **   extend the escape-code space later if we like).
  76. **
  77. ** You have to construct these line-2 arg lines carefully. For example,
  78. ** beware of trailing spaces at the end of the line. They'll give you
  79. ** extra trailing empty-string args.
  80. **
  81. ** You should also beware of including nul bytes into your arguments, since
  82. ** C's pathetic excuse for a string data-type will lose if you try this.
  83. **
  84. **
  85. ** Another way to get this sort of multiple-argument functionality, with
  86. ** the extra cost of starting up a shell, is to simply have the following
  87. ** trampoline at the beginning of your script:
  88. **     #!/bin/sh -
  89. **     exec /usr/local/bin/scheme -heap 4000000 -batch -script $0 $*
  90. **     !#
  91. ** (or use the indir program, same rough idea). This is less appropriate
  92. ** for interpreters intended to replace the shell.
  93. **
  94. ** Possible extensions:
  95. ** - I considered making the argument line syntax hairier -- adding ~user
  96. **   directory expansion and $(envvar) expansion. But I didn't do it.
  97. **
  98. ** - Not much error information. If something is wrong -- file can't
  99. **   be read, no second line, illegal syntax on second line, malloc
  100. **   loses -- you just get a NULL return value. You can examine errno
  101. **   if the problem is a Unix error (e.g., file error). But if the call
  102. **   fails for another reason (e.g., bad arg syntax on the second line),
  103. **   then errno won't help. This code could be modified to take an additional
  104. **   &error_code argument, and assign an integer into the var indicating
  105. **   just exactly what the problem was, if that's important to your 
  106. **   application. 
  107. **
  108. ** This code is fairly robust, careful code. ANSI standard C. No dependencies 
  109. ** on fixed-size buffers. It won't blow up if the inputs are pathological.
  110. ** It all type-checks. No core leaks. Feel free to customise it for the
  111. ** particular needs of a given interpreter; the core functionality is there.
  112. **
  113. ** See the end of this file for a sample program with an arg processing loop.
  114. ** Please send me bug reports, fixes, and improvements.
  115. **
  116. ** Some interpreters that might use this: tcl (wish, hope), perl, Smalltalk,
  117. ** little Schemes (scm, elk, s48, ...), big Schemes, Postscript, emacs, 
  118. ** Dylan, Lisp, Prolog.
  119. **     -Olin Shivers 2/93
  120. **     shivers@cs.cmu.edu
  121. **     shivers@csd.hku.hk
  122. */
  123.  
  124. #include <stdio.h>
  125. #include <stdlib.h> /* malloc */
  126. #include <ctype.h>
  127.  
  128. #define Alloc(type)     ((type *) malloc(sizeof(type)))
  129. #define Malloc(type,n)    ((type *) malloc(sizeof(type)*(n)))
  130. #define Realloc(type,ptr,size) \
  131.     ((type *) realloc((void *)ptr, sizeof(type)*(size)))
  132. #define Free(p)        (free((void *)(p)))
  133.  
  134. /* Is character c an octal digit? */
  135. #define isodigit(c) (isdigit(c) && (c) != '8' && (c) != '9')
  136.  
  137. /* Double the vector if we've overflowed it. Return the vector.
  138. ** If we double the vector, lenptr is updated with the new length.
  139. ** If we fail, return NULL.
  140. */
  141.  
  142. static void *maybe_grow_vec(void *vec, int *lenptr, int index, int elt_size)
  143. {
  144.     int len = *lenptr;
  145.     if( index < len ) return vec;
  146.     len *= 2;
  147.     *lenptr = len; /* Update the length pointer. */
  148.     return realloc(vec, len*elt_size);
  149.     }
  150.  
  151. /* The do ... while(0) is a trick to make this macro accept a terminating
  152. ** semicolon.
  153. */
  154. #define Maybe_Grow_Vec(vec, size, index, elt_t, lose) \
  155.     do {elt_t *mgv_tmp =(elt_t*)maybe_grow_vec((void*)vec, &size, \
  156.                            index, sizeof(elt_t)); \
  157.     if(mgv_tmp) vec = mgv_tmp; else goto lose;} while (0);
  158.  
  159.  
  160. /* process_meta_arg(fname, av)
  161. ** -----------------------
  162. ** The main routine.
  163. **
  164. ** Expand a \ <fname> switch. Return NULL on error, otherwise a new arg
  165. ** vector composed of (1) the args scanned in from line 2 of fname, followed
  166. ** by (2) the arguments in av. The argument vector av starts with the
  167. ** argument following the \ switch, i.e., the <fname> argument.
  168. */
  169.  
  170. static char* read_arg(FILE*);
  171.  
  172. char **process_meta_arg(char **av)
  173. {
  174.     char **argv, *arg, **ap;
  175.     int c;
  176.     FILE *script;
  177.     char *fname;
  178.     int av_len;
  179.     int argv_i=0, argv_len=100;
  180.  
  181.     if( !*av ) return NULL;
  182.     fname = *av;
  183.     script = fopen(fname, "r");
  184.     if( !script ) return NULL;
  185.  
  186.     /* Skip line 1. */
  187.     while( '\n' != getc(script) )
  188.     if( feof(script) || ferror(script) ) goto lose3;
  189.  
  190.     argv = Malloc(char*, argv_len);
  191.     if( !argv ) goto lose3;
  192.  
  193.     while( EOF != (c=getc(script)) && '\n' != c ) {
  194.     char *arg;
  195.     ungetc(c,script);
  196.     arg = read_arg(script);
  197.     if( !arg ) goto lose2;
  198.     Maybe_Grow_Vec(argv, argv_len, argv_i, char*, lose1);
  199.     argv[argv_i++] = arg;
  200.     }
  201.  
  202.     for(av_len=0; av[av_len]; av_len++);    /* Compute length of av. */
  203.  
  204.     /* Precisely re-size argv. */
  205.     if( NULL == (ap=Realloc(char*, argv, argv_len + av_len + 1)) ) goto lose2;
  206.     argv = ap;
  207.  
  208.     while( argv[argv_i++] = *av++ );    /* Copy over av & null terminate. */
  209.  
  210.     fclose(script);
  211.     return argv;
  212.  
  213.  
  214.     /* Exception handlers: free storage and lose. */
  215.   lose1:
  216.     Free(arg);
  217.   lose2:
  218.     while( argv_i ) Free(argv[--argv_i]);
  219.     Free(argv);
  220.  lose3:
  221.     fclose(script);
  222.     return NULL;
  223.     }
  224.  
  225. /* Read in one arg and it's terminating space.
  226. ** If arg is terminated by a newline, leave the newline in
  227. ** the stream so the outer loop can see it. Return a newly-allocated
  228. ** string containing the arg; NULL if there's an error.
  229. */
  230. static char *read_arg(FILE *f)
  231. {
  232.     char *buf, *tmp;
  233.     int buflen, i;
  234.  
  235.     /* Allocate a buffer for the arg. */
  236.     i = 0;
  237.     buflen=20;
  238.     if( !(buf = Malloc(char, buflen)) ) return NULL;
  239.  
  240.     /* Read in the arg. */
  241.     while(1) {
  242.     int c = getc(f);
  243.  
  244.     if( c == EOF || c == ' ' ) break;
  245.     if( c == '\n' ) {ungetc(c, f); break;}
  246.  
  247.     /* Do knock-down processing. */
  248.     if( c == '\\' ) {
  249.         int c1, c2, c3;
  250.         switch (c1=getc(f)) {
  251.           case EOF:
  252.         goto lose;
  253.  
  254.         /* \nnn octal escape. */
  255.           case '0':        case '1':
  256.           case '2':        case '3':
  257.           case '4':        case '5':
  258.           case '6':        case '7':
  259.         if( EOF == (c2=getc(f)) || !isodigit(c2) ) goto lose;
  260.         if( EOF == (c3=getc(f)) || !isodigit(c3) ) goto lose;
  261.         c = ((c1-'0')<<6) | ((c2-'0')<<3) | (c3-'0');
  262.         break;
  263.  
  264.         /* ANSI C escapes. */
  265.           case 'n':    c='\n'; break;
  266.           case 'r':    c='\r'; break;
  267.           case 't':    c='\t'; break;
  268.           case 'b':    c='\b'; break;
  269.  
  270.         /* Simple knock-down: \, space, tab, newline. */
  271.           case '\\':    case ' ':
  272.           case '\t':    case '\n':
  273.         c=c1; break;
  274.  
  275.         /* Nothing else allowed. */
  276.           default: goto lose;
  277.         }
  278.         }
  279.  
  280.     /* No tab allowed. */
  281.     else if( c == '\t' ) goto lose;
  282.  
  283.     Maybe_Grow_Vec(buf, buflen, i, char, lose);
  284.     buf[i++] = c;
  285.     }
  286.  
  287.     /* Null terminate the arg. */
  288.     Maybe_Grow_Vec(buf, buflen, i, char, lose);
  289.     buf[i++] = '\0';
  290.  
  291.     /* Precisely re-size buf and return. */
  292.     if( tmp=Realloc(char,buf,i) ) return tmp;
  293.  
  294.   lose:
  295.     Free(buf);
  296.     return NULL;
  297.     }
  298.  
  299.  
  300. /*****************************************************************************/
  301. #if 0
  302. /*
  303. ** Debugging test stub and example argument scanner. 
  304. ** Like echo, but with \ <fname> expansion.
  305. **/
  306.  
  307. char *prog_name;
  308.  
  309. static void usage(void)
  310. {
  311.     fprintf(stderr,
  312.         "Usage: %s [\\ <fname>] [-n] [--] arg1 ... argn\n",
  313.         prog_name);
  314.     exit(1);
  315.     }
  316.  
  317. /* Expand away a leading meta-arg if there is one. Die informatively on error.
  318. ** I can't think of a reason why you might want to have recursive meta
  319. ** arguments, but we handle this case to be complete.
  320. */
  321. static char **maybe_expand_meta_arg(char **argv)
  322. {
  323.     if( *argv )
  324.     while( strcmp(*argv, "\\") == 0 ) {
  325.         argv++;
  326.         if( !*argv ) {
  327.         fprintf(stderr, "%s: \\ switch without following filename.\n",
  328.             prog_name);
  329.         usage();
  330.         }
  331.         argv = process_meta_arg(argv);
  332.         if( !argv ) {
  333.         fprintf(stderr, "%s: unable to expand \\ <filename> switch.\n",
  334.             prog_name);
  335.         usage();
  336.         }
  337.         }
  338.     return argv;
  339.     }
  340.  
  341. main(int argc, char **argv)
  342. {
  343.     int n_flag=0;
  344.  
  345.     prog_name = *argv++;
  346.  
  347.     /* Handle an initial meta-arg expansion. */
  348.     argv = maybe_expand_meta_arg(argv);
  349.  
  350.     /* Process switches. */
  351.     for(;*argv;argv++) {
  352.     /* Process arg. */
  353.     if( argv[0][0] == '-' )
  354.         switch( argv[0][1] ) {
  355.           /* -n means no terminating newline. */
  356.           case 'n':
  357.         n_flag++;
  358.         break;
  359.  
  360.           /* -- terminates args, so you can echo \, -n, -- args. */
  361.           case '-':
  362.         argv++;
  363.         goto args_done;
  364.         break;
  365.  
  366.           default:
  367.         fprintf(stderr, "%s: unknown flag %s.\n", prog_name, *argv);
  368.         usage();
  369.           }
  370.     else goto args_done; /* Not a switch. We are done. */
  371.     }
  372.  
  373.   args_done:
  374.     if( *argv ) printf("\"%s\"", *argv++);
  375.     while( *argv ) printf(" \"%s\"", *argv++);
  376.     if( !n_flag ) putchar('\n');
  377.     }
  378. #endif /* 0 */
  379.